package com.tools;

import java.io.IOException;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class ByteTool {

	final static char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
			'9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
			'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
			'z' };

	// static char[] base64_array = new char[64];
	static char[] base64_array = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
			'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
			'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
			'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
			'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8',
			'9', '+', '/' };

	private static int[] base64_decode_array = new int[128];
	static {

		int i = 0;

		for (; i < base64_decode_array.length; i++)
			base64_decode_array[i] = -1;

		i = 0;

		for (char c = 'A'; c <= 'Z'; c++)
			base64_decode_array[c] = i++;
		for (char c = 'a'; c <= 'z'; c++)
			base64_decode_array[c] = i++;
		for (char c = '0'; c <= '9'; c++)
			base64_decode_array[c] = i++;
		base64_decode_array['+'] = i++;
		base64_decode_array['/'] = i++;
	}

	public static void main(String[] args) throws IOException {

		System.out.println(Integer.toBinaryString(0xFF));

		byte[] bs = new byte[] { (byte) 0xf2, 0x0d, 0x32, 0x33 };

		printByteByHex(bs, false);

		bs = shift(bs, 36);

		printByteByHex(bs, false);

		bs = getByte(bs, 12, 20, (byte) 0x71);

		printByteByHex(bs, false);

		// long t1 = 0l;
		// long t3 = 0l;
		// BASE64Encoder encoder = new BASE64Encoder();
		// for(int i = 0;i< 10000;i++){
		// String str = StringUtils.randomString(100, 10000);
		//
		// long s = System.currentTimeMillis();
		// toBase64String(str.getBytes());
		// t1 += System.currentTimeMillis()-s;
		//
		//
		// s = System.currentTimeMillis();
		// encoder.encode(str.getBytes());
		// t3 += System.currentTimeMillis()-s;
		//
		//
		// }
		// System.out.println("my function : "+t1+" ms");
		// System.out.println("officail function : "+t3 +" ms");

		long t1 = 0l;
		long t3 = 0l;
		BASE64Decoder decoder = new BASE64Decoder();
		for (int i = 0; i < 10000; i++) {
			String str = StringUtils.randomString(100, 10000);

			str = toBase64String(str.getBytes());

			long s = System.currentTimeMillis();
			new String(Base64toByte(str.getBytes()));
			t1 += System.currentTimeMillis() - s;

			s = System.currentTimeMillis();
			decoder.decodeBuffer(str);
			t3 += System.currentTimeMillis() - s;

		}
		System.out.println("my function : " + t1 + " ms");
		System.out.println("officail function : " + t3 + " ms");

		String str = "what the fuckaijn[oqwfnh[2303jnvkl;mwkkjf2nfifjij顶你个肺";
		String s1 = toBase64String(str.getBytes());
		for (int i = 0; i < s1.length(); i++) {
			if (i % 4 == 0)
				System.out.print("\t");
			System.out.print(s1.charAt(i));

		}
		// System.out.println();
		// s1 = new String(toBase64Chars(str.getBytes()));
		// for(int i=0;i<s1.length();i++){
		// if(i%4==0)System.out.print("\t");
		// System.out.print(s1.charAt(i));
		//
		// }
		//
		System.out.println();
		String s2 = new BASE64Encoder().encode(str.getBytes());
		for (int i = 0; i < s2.length(); i++) {
			if (i % 4 == 0)
				System.out.print("\t");
			System.out.print(s2.charAt(i));

		}
		System.out.println();

		System.out.println(new String(Base64toByte(s1.getBytes())));

		BASE64Decoder de = new BASE64Decoder();
		str = new String(de.decodeBuffer(s1));
		System.out.println(str);
	}

	public static String HexStringToString(String string) {
		return new String(HexStringToByte(string));
	}

	/***
	 * 
	 * covert Hex String to byte
	 * 
	 * @param string
	 * @return
	 */
	public static byte[] HexStringToByte(String string) {
		int sp = 2;
		if (string == null || string.trim().equals(""))
			return null;
		byte[] bs = new byte[string.length() / 2];

		// every byte occupancy two hex code
		if (string.length() % 2 != 0)
			return null;

		int n = 0;
		for (int i = 0; i < string.length(); i += sp) {
			String s = null;
			s = string.substring(i, i + sp);
			bs[n++] = (byte) Integer.parseInt(s, 16);
		}
		return bs;
	}

	public static void printByteByHex(byte[] bs) {
		printByteByHex(bs, true);
	}

	public static void printByteByHex(byte[] bs, boolean desc) {
		if (bs == null || bs.length < 1)
			return;
		int l = bs.length - 1;
		for (int i = 0; i < bs.length; i++) {
			if (i % 16 == 0)
				System.out.println();
			System.out.format((((desc ? bs[i] : bs[l - i]) & 0xF0) > 0 ? ""
					: "0") + "%X\t", (desc ? bs[i] : bs[l - i]));
		}
		System.out.println();
	}

	/***
	 * 
	 * 
	 * 
	 * cover hex byte code to string ,eg : 11111111 cover to String 'FF'
	 * 
	 * @param bs
	 * @return hex String
	 */
	public static String toHexString(byte[] bs) {
		return toHexString(bs, "");
	}

	/***
	 * 
	 * 
	 * 
	 * cover hex byte code to string ,eg : 11111111 cover to String 'FF'
	 * 
	 * @param bs
	 *            : hex code , separator : the separator char between every two
	 *            hex code
	 * @return hex String
	 */

	public static String toHexString(byte[] bs, String seprator) {
		StringBuffer sb = new StringBuffer();

		int shift = 4;
		int length = 8 / shift;
		char[] buf = new char[length];
		int radix = 1 << shift;
		int mask = radix - 1;
		for (int i = 0; i < bs.length; i++) {
			byte b = bs[i];
			for (int l = 0; l < buf.length; l++) {
				buf[l] = '0';
			}
			int charPos = length;
			int n = 0;
			do {
				buf[--charPos] = digits[b & mask];
				b >>>= shift;
				n++;
			} while (b != 0 && n < length);
			sb.append(new String(buf).toUpperCase() + seprator);
		}
		return sb.toString();
	}

	/***
	 * 
	 * 
	 * shift right every digital
	 * 
	 * @param bs
	 *            byte code
	 * @param shift
	 *            shift count
	 * @return
	 */

	public static byte[] shift(byte[] bs, int shift) {

		int byteLength = 8;
		if (bs == null || bs.length < 1 || shift < 1)
			return bs;

		// if shift bigger than the length of byte ,then shift array
		int arrayShift = shift / byteLength;

		bs = shiftArray(bs, arrayShift);

		shift %= byteLength;

		byte[] out = new byte[bs.length];
		byte paraLeft = (byte) (Math.pow(2, shift) - 1);
		byte paraRight = (byte) (0xFF >>> shift);
		byte pb = 0;
		pb = (byte) (bs[0] & paraLeft);
		pb = (byte) (pb << (byteLength - shift));
		out[0] = (byte) ((bs[0] >>> shift) & paraRight);
		for (int i = 1; i < bs.length; i++) {
			byte b = (byte) (bs[i] & paraLeft);
			b = (byte) (b << (byteLength - shift));
			out[i - 1] = (byte) (out[i - 1] | b);
			out[i] = (byte) ((bs[i] >>> shift) & paraRight);
			out[i] = (byte) (bs[i] >>> shift);
		}
		out[out.length - 1] = (byte) (out[out.length - 1] | pb);
		return out;
	}

	/***
	 * 
	 * 
	 * shift right every byte
	 * 
	 * @param bs
	 *            byte code
	 * @param shift
	 *            shift count
	 * @return
	 */
	public static byte[] shiftArray(byte[] objs, int shift) {

		if (objs == null || objs.length < 1 || shift < 1)
			return objs;
		byte[] res = new byte[objs.length];
		shift %= objs.length;
		System.arraycopy(objs, shift, res, 0, objs.length - shift);
		System.arraycopy(objs, 0, res, shift, shift);
		return res;
	}

	public static String toBase64String(byte[] gs) {
		int index = 0;
		int length = gs.length;
		char[] cb = new char[((gs.length + 2) / 3) * 4];
		int i = 0;
		while (index < length) {
			cb[i++] = base64_array[(gs[index] >>> 2) & 0x3F];
			byte b0 = gs[index];
			byte b1 = 0;
			b1 = ++index == length ? 0 : gs[index];
			cb[i++] = base64_array[(b0 & 0x03) << 4 | (b1 >>> 4 & 0x0F)];
			b0 = b1;
			if (++index <= length) {
				b1 = index == length ? 0 : gs[index];
				cb[i++] = base64_array[((b0 & 0x0F) << 2) | (b1 >>> 6 & 0x03)];
			} else {
				cb[i++] = '=';
			}
			cb[i++] = index < length ? base64_array[b1 & 0x3F] : '=';
			index++;
		}
		return new String(cb);
	}

	public static byte[] Base64toByte(byte[] bs) {
		if (bs == null || bs.length % 4 != 0)
			return null;
		int length = bs.length;
		int charNum = 0;
		for (int i = length - 1; i >= 0; i--) {
			if (bs[i] == '=')
				charNum++;
			else
				break;
		}
		byte[] res = new byte[length / 4 * 3 - charNum];

		int index = 0;
		int rIndex = 0;
		while (index < length) {
			res[rIndex] = (byte) (base64_decode_array[bs[index++]] << 2);
			res[rIndex] = (byte) (res[rIndex++] | (base64_decode_array[bs[index]] >>> 4) & 0x03);
			if (rIndex >= res.length)
				break;
			res[rIndex] = (byte) (base64_decode_array[bs[index++]] << 4);
			if (index == length || bs[index] == '=')
				break;
			res[rIndex] = (byte) (res[rIndex++] | (base64_decode_array[bs[index]] >>> 2) & 0x0F);
			if (rIndex >= res.length)
				break;
			res[rIndex] = (byte) (base64_decode_array[bs[index++]] << 6);
			if (index == length || bs[index] == '=')
				break;
			res[rIndex] = (byte) (res[rIndex++] | base64_decode_array[bs[index++]]);
			if (index == length || bs[index] == '=' || rIndex >= res.length)
				break;
		}

		return res;
	}

	/***
	 * 
	 * 
	 * 
	 * @param bs
	 * @param from
	 * @param length
	 * @param covert
	 * @return
	 */
	public static byte[] getByte(byte[] bs, int from, int length, byte covert) {

		length = Math.abs(length);
		from = Math.abs(from);

		// sum is byte count, 1 byte equal to 8 bit
		int byteLength = 8;
		int sum = (length + byteLength - 1) / byteLength;

		byte[] res = new byte[sum];
		if (bs == null)
			return res;
		if (from >= bs.length * byteLength
				|| from + length > bs.length * byteLength)
			throw new java.lang.ArrayIndexOutOfBoundsException();

		int i = 0;
		int index = from / byteLength;
		int shift = from % byteLength;
		int endIndex = (from + length + 7) / 8;

		do {
			if (shift >= 0) {
				byte paraRight = (byte) (0xFF >>> shift);
				res[i] = (byte) ((bs[index] >>> shift) & paraRight);
				shift -= byteLength;
				index++;
			} else {
				res[i] |= bs[index] << -shift;
				shift += byteLength;
				i++;
			}
		} while (index < endIndex && i < res.length);

		i = sum - 1;
		shift = length % byteLength;
		if (shift != 0)
			res[i] = (byte) (res[i] & (0xFF >>> (byteLength - shift)) | 0xFF & (covert << shift));
		return res;
	}

}
